const std = @import("std");
const Index = @import("index.zig");

const Cell = @import("../cell.zig");
const Food = @import("food.zig");

const IndexBlock = @This();

up: ?*IndexBlock,
right_up: ?*IndexBlock,
right: ?*IndexBlock,
right_down: ?*IndexBlock,
down: ?*IndexBlock,
left_down: ?*IndexBlock,
left: ?*IndexBlock,
left_up: ?*IndexBlock,

index: *Index,

cell_set: std.AutoHashMap(u32, *Cell),
food_set: std.AutoHashMap(u32, *Food),

pub fn init(index: *Index) *@This() {
    const item = index.allocator.create(IndexBlock) catch @panic("Out of memory");

    item.* = .{
        .up = null,
        .right_up = null,
        .right = null,
        .right_down = null,
        .down = null,
        .left_down = null,
        .left = null,
        .left_up = null,
        .index = index,
        .cell_set = std.AutoHashMap(u32, *Cell).init(index.allocator),
        .food_set = std.AutoHashMap(u32, *Food).init(index.allocator),
    };

    return item;
}

pub fn add(self: *@This(), comptime T: type, item: *T) void {
    switch (T) {
        Cell => {
            self.cell_set.put(item.id, item) catch {};
        },
        Food => {
            self.food_set.put(item.id, item) catch {};
        },
        else => {},
    }
}

pub fn remove(self: *@This(), comptime T: type, item: *T) void {
    switch (T) {
        Cell => {
            _ = self.cell_set.remove(item.id);
        },
        Food => {
            _ = self.food_set.remove(item.id);
        },
        else => {},
    }
}

pub fn destroy(self: *@This(), comptime T: type, item: *T) void {
    self.remove(T, item);
    item.deinit();
}

pub fn deinit(self: *@This()) void {
    var cell_iter = self.cell_set.iterator();
    while (cell_iter.next()) |item| {
        item.value_ptr.*.deinit();
    }
    self.cell_set.deinit();

    var food_iter = self.food_set.iterator();
    while (food_iter.next()) |item| {
        item.value_ptr.*.deinit();
    }
    self.food_set.deinit();

    self.index.allocator.destroy(self);
}

pub fn prepare(self: *@This()) void {
    var food_iter = self.food_set.iterator();
    while (food_iter.next()) |item| {
        item.value_ptr.*.prepare();
    }

    var cell_iter = self.cell_set.iterator();
    while (cell_iter.next()) |item| {
        item.value_ptr.*.prepare();
    }
}

pub fn do(self: *@This()) void {
    var food_iter = self.food_set.iterator();
    while (food_iter.next()) |item| {
        item.value_ptr.*.do();
    }

    var cell_iter = self.cell_set.iterator();
    while (cell_iter.next()) |item| {
        item.value_ptr.*.do();
    }
}

pub fn post(self: *@This()) void {
    var food_iter = self.food_set.iterator();
    while (food_iter.next()) |item| {
        item.value_ptr.*.post();
    }

    var cell_iter = self.cell_set.iterator();
    while (cell_iter.next()) |item| {
        item.value_ptr.*.post();
    }
}

pub fn draw(self: *@This()) void {
    var food_iter = self.food_set.iterator();
    while (food_iter.next()) |item| {
        item.value_ptr.*.draw();
    }

    var cell_iter = self.cell_set.iterator();
    while (cell_iter.next()) |item| {
        item.value_ptr.*.draw();
    }
}
